//本工具封装文件类型聊天信息的各种方法
import { reqReadFile } from '@/api/file/index.ts'
import { getDirectoryHandleFromIndexedDB } from './createdir'
import { ElMessage } from 'element-plus'
import { PreviewImg } from '@/api/file/type'
import useErrorStore from '@/store/modules/error'

//获取文件类型的MIME类型
const getFileMimeType = (message_type: string, fileName: string) => {
    //根据文件扩展名判断文件类型
    const ext = fileName.split('.').pop()?.toLowerCase()
    switch (message_type) {
        case 'audio':
            return `audio/${ext}`
        case 'video':
            return `video/${ext}`
        case 'picture':
            return `image/${ext}`
        case 'emoji':
            return 'text/plain'
        case 'file':
            return `application/${ext}`
        default:
            return 'application/octet-stream'
    }
}

//根据后缀名判断文件类型
export const determineFileType = (ext: string) => {
    switch (ext) {
        case 'mp3':
        case 'wav':
        case 'ogg':
            return 'audioFile' //音频文件

        case 'mp4':
        case 'avi':
        case 'mkv':
        case 'mov':
            return 'video' //视频文件

        case 'png':
        case 'jpg':
        case 'jpeg':
        case 'gif':
        case 'bmp':
            return 'picture' //图片文件

        case 'emoji':
            return 'emoji' //表情文件

        default:
            return 'file' //其他文件类型
    }
}

//根据file类型文件的后缀名判断文件缩略图类型
export const determineFileImage = (ext: string) => {
    switch (ext) {
        case 'doc':
        case 'docx':
            return '/public/file-images/word.png' //word文档

        case 'pdf':
            return '/public/file-images/pdf.png' //pdf文档

        case 'xls':
        case 'xlsx':
            return '/public/file-images/excel.png' //excel文档

        case 'ppt':
        case 'pptx':
            return '/public/file-images/ppt.png' //ppt文档

        case 'txt':
            return '/public/file-images/txt.png' //txt文档

        case 'zip':
        case 'rar':
            return '/public/file-images/compress.png' //压缩文件

        default:
            return '/public/file-images/unknown-file.png' //未知类型文档
    }
}

//封装函数读取blob并返回base64
export const readBlobAsBase64 = (blob: Blob): Promise<string> => {
    return new Promise((resolve, reject) => {
        const reader = new FileReader()
        reader.readAsDataURL(blob)
        reader.onloadend = () => {
            //去掉 'data:......;base64,' 部分
            const base64Data = (reader.result as string).split(',')[1]
            resolve(base64Data)
        }
        reader.onerror = reject
    })
}

// 将 Base64 字符串转换为 File 对象
export const base64ToFile = (base64String: string, fileName: string): File => {
    // 1. 提取 MIME 类型和 Base64 数据
    const matches = base64String.match(/^data:(.+);base64,(.+)$/)
    if (!matches || matches.length !== 3) {
        throw new Error('Invalid Base64 string')
    }

    const mimeType = matches[1] // 获取 MIME 类型
    const base64Data = matches[2] // 获取 Base64 数据

    // 2. 将 Base64 数据解码为二进制数据
    const byteCharacters = atob(base64Data)

    // 3. 将二进制数据转换为 Uint8Array
    const byteNumbers = new Array(byteCharacters.length)
    for (let i = 0; i < byteCharacters.length; i++) {
        byteNumbers[i] = byteCharacters.charCodeAt(i)
    }
    const byteArray = new Uint8Array(byteNumbers)

    // 4. 创建 Blob 对象
    const blob = new Blob([byteArray], { type: mimeType })

    // 5. 将 Blob 转换为 File 对象
    const file = new File([blob], fileName, { type: mimeType })

    return file
}

// 获取图片的宽和高
// 获取图片的宽和高
export function getImageDimensions(
    imageUrl: string,
): Promise<{ image: HTMLImageElement; width: number; height: number }> {
    return new Promise((resolve, reject) => {
        const img = new Image() // 创建一个 Image 对象

        img.onload = function () {
            resolve({
                image: img, // 图片实例
                width: img.width, // 获取图片宽度
                height: img.height, // 获取图片高度
            })
        }

        img.onerror = function () {
            reject(new Error('图片加载失败'))
        }

        img.src = imageUrl // 设置图片的 src
    })
}

//文件保存本地的方法
export async function saveFile(message_type: string, blob: Blob, fileName: string) {
    try {
        // 从IndexedDB获取主目录句柄
        const directoryHandle = await getDirectoryHandleFromIndexedDB()

        if (!directoryHandle) {
            return ElMessage.error('未找到存储文件的目录句柄')
        }

        //获取子目录句柄
        const chatAppDirHandle = await directoryHandle.getDirectoryHandle('chatApp')
        const audioDirHandle = await chatAppDirHandle.getDirectoryHandle(message_type)
        //创建或获取要保存的文件句柄
        const fileHandle = await audioDirHandle.getFileHandle(fileName, {
            create: true,
        })
        //创建可写文件流
        const writable = await fileHandle.createWritable()
        //将数据写入文件
        await writable.write(blob)
        //关闭文件流
        await writable.close()
    } catch (error) {
        ElMessage.error('保存文件失败')
    }
}

//加载聊天文件的方法
export const loadFile = async (message_type: string, media_url: string) => {
    const errorStore = useErrorStore()
    //创建一个文件访问路径
    let Url = null
    //分割出文件名
    const lastPart: string = media_url.split('/').pop()!
    try {
        // 从IndexedDB获取存储的主目录句柄
        const directoryHandle = await getDirectoryHandleFromIndexedDB()
        if (!directoryHandle) {
            throw new Error('未找到存储文件的目录句柄，将请求服务器资源')
        }

        // 获取子目录句柄
        const chatAppDirHandle = await directoryHandle.getDirectoryHandle('chatApp')
        const childrenDirHandle = await chatAppDirHandle.getDirectoryHandle(message_type)
        // 获取相应的文件句柄
        const fileHandle = await childrenDirHandle
            .getFileHandle(lastPart, { create: false })
            .catch(() => null)
        if (fileHandle) {
            // 如果文件存在于本地，则读取本地文件
            const file = await fileHandle.getFile()
            const fileBlob = new Blob([file], {
                type: getFileMimeType(message_type, lastPart),
            })
            //令URL为文件访问路径
            Url = URL.createObjectURL(fileBlob)
        } else {
            //发起获取文件请求
            const result = await reqReadFile(lastPart)
            //将获取的数据转为Uint8Array
            const arrayBuffer = new Uint8Array(result.data.data)
            //创建Blob对象，指定类型
            const fileBlob = new Blob([arrayBuffer], {
                type: getFileMimeType(message_type, lastPart),
            })
            //令URL为文件访问路径
            Url = URL.createObjectURL(fileBlob)
        }
    } catch (error: any) {
        const errorMessage = '文件加载失败'
        if (errorStore.errorMessage !== errorMessage) {
            ElMessage.error(errorMessage)
        }
        errorStore.errorMessage = errorMessage
    }
    return Url
}

//根据上传文件的字节大小规范化文件的大小显示
export const calcFileSize = (size: number) => {
    let fileSize: string = ''
    if (size < 1024) {
        fileSize = size.toString() + ' B'
    } else if (size >= 1024 && size < 1048576) {
        fileSize = (size / 1024).toFixed(2) + ' KB'
    } else {
        fileSize = (size / 1024 / 1024).toFixed(2) + ' MB'
    }
    return fileSize
}

//计算语音气泡的宽度的方法
export const calculateBubbleWidth = (duration: number) => {
    const minWidth = 50 //最小宽度 (px)
    const maxWidth = 300 //最大宽度 (px)
    const maxDuration = 60 //当音频时长达到 60 秒时，达到最大宽度
    //计算宽度，线性映射时长到宽度
    const width = (duration / maxDuration) * (maxWidth - minWidth) + minWidth
    return Math.min(width, maxWidth) //确保宽度不超过最大值
}

//生成视频预览图的方法
export const generateVideoThumbnail = (videoUrl: string) => {
    return new Promise<PreviewImg>((resolve, reject) => {
        const video = document.createElement('video')
        video.src = videoUrl

        video.addEventListener('loadeddata', () => {
            //设置视频帧时间
            video.currentTime = 0.001

            video.addEventListener('seeked', () => {
                const canvas = document.createElement('canvas')
                canvas.width = video.videoWidth
                canvas.height = video.videoHeight
                const ctx = canvas.getContext('2d')
                ctx?.drawImage(video, 0, 0, canvas.width, canvas.height)

                //将canvas转为Base64图片数据URL
                const imageUrl = canvas.toDataURL('image/png')
                video.remove()
                resolve({
                    previewUrl: imageUrl,
                    fileWidth: canvas.width,
                    fileHeight: canvas.height,
                })
            })
        })

        video.addEventListener('error', () => {
            video.remove()
            reject('无法加载视频文件')
        })
    })
}

//下载文件的方法
export const downloadFile = (url: string, fileName: string) => {
    const link = document.createElement('a')
    link.href = url
    link.download = fileName // 指定下载文件的名称
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
}
